﻿//TODO Gray2Color: test

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Control point struct
    /// </summary>
    public struct ControlPoint
    {
        private int m_nLevel;
        private Color m_Color;

        /// <summary>
        /// Gets or sets the level.
        /// </summary>
        /// <value>The level.</value>
        public int Level
        {
            get { return m_nLevel; }
            set { m_nLevel = value; }
        }

        /// <summary>
        /// Gets or sets the color.
        /// </summary>
        /// <value>The color.</value>
        public Color Color
        {
            get { return m_Color; }
            set { m_Color = value; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ControlPoint"/> struct.
        /// </summary>
        /// <param name="_nLevel">The _n level.</param>
        /// <param name="_color">The _color.</param>
        public ControlPoint(int _nLevel, Color _color)
        {
            this.m_Color = _color;
            this.m_nLevel = _nLevel;
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return "Level: " + m_nLevel.ToString() + "; Color: " + m_Color.ToString();
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public class PointsComparer : IComparer<ControlPoint>
    {
        #region IComparer<ControlPoint> Members
        /// <summary>
        /// Vergleicht zwei Objekte und zeigt über den zurückgegebenen Wert an, ob eines der Objekte kleiner, gleich oder größer als das andere Objekt ist.
        /// </summary>
        /// <param name="x">Das erste zu vergleichende Objekt.</param>
        /// <param name="y">Das zweite zu vergleichende Objekt.</param>
        /// <returns>
        /// Wert
        /// Bedingung
        /// Kleiner als 0 (null)
        /// <paramref name="x"/> ist kleiner als <paramref name="y"/>.
        /// 0
        /// <paramref name="x"/> ist gleich <paramref name="y"/>.
        /// Größer als 0 (null)
        /// <paramref name="x"/> ist größer als <paramref name="y"/>.
        /// </returns>
        public int Compare(ControlPoint x, ControlPoint y)
        {
            if (x.Level > y.Level)
            {
                return 1;
            }
            if (x.Level < y.Level)
            {
                return -1;
            }
            return 0;
        }
        #endregion
    }

    /// <summary>
    /// Use this to change a gray picture into a colored picture
    /// </summary>
    public class Gray2Color : BaseImageFilter
    {
        protected List<ControlPoint> m_aControlPoint = new List<ControlPoint>();

        /// <summary>
        /// If a constructor with no parameters is used
        /// we get ourself some controlpoints
        /// </summary>
        public Gray2Color()
        {
            m_aControlPoint.Add(new ControlPoint(127, Color.Red));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_aControlPoint"></param>
        public Gray2Color(List<ControlPoint> _aControlPoint)
        {
            m_aControlPoint = _aControlPoint;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Gray2Color"/> class.
        /// </summary>
        /// <param name="_gray2Color">Color of the _gray2.</param>
        internal Gray2Color(Gray2Color _gray2Color)
        {
            this.m_aControlPoint = _gray2Color.m_aControlPoint;
        }

        /// <summary>
        /// This tries to convert a given gray image to a colored image again.
        /// To be able to do this, the color diagram needs some control paints.
        /// With those set, 
        /// </summary>
        /// <param name="_bitmap"></param>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap sourceBitmap = _bitmap;

            Color[] colors = GetColorDiagram(m_aControlPoint);

            int nHeight = sourceBitmap.Height;
            int nWidth = sourceBitmap.Width;

            int nLevel = -1;

            int x = 0;
            int j = 0;

            for (x = 0; x < nWidth; x++)
            {
                for (int y = 0; y < nHeight; y++)
                {
                    nLevel = sourceBitmap.GetPixel(x, y).B;
                    sourceBitmap.SetPixel(x, y, colors[nLevel]);
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Color Diagram
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        public static Color[] GetColorDiagram(List<ControlPoint> points)
        {
            Color[] aColor = new Color[256];
            points.Sort(new PointsComparer());

            ControlPoint leftColor;
            ControlPoint rightColor;

            int i = 0;
            for (i = 0; i < 256; i++)
            {
                leftColor = new ControlPoint(0, ColorFunctions.GetNearestLeftColor(points[0].Color)); //new ControlPoint(0, Color.Black);
                rightColor = new ControlPoint(255, ColorFunctions.GetNearestRightColor(points[points.Count - 1].Color));
                if (i < points[0].Level)
                {
                    rightColor = points[0];
                }
                if (i > points[points.Count - 1].Level)
                {
                    leftColor = points[points.Count - 1];
                }
                else
                {
                    for (int j = 0; j < points.Count - 1; j++)
                    {
                        if ((points[j].Level <= i) & (points[j + 1].Level > i))
                        {
                            leftColor = points[j];
                            rightColor = points[j + 1];
                        }
                    }
                }
                if ((rightColor.Level - leftColor.Level) != 0)
                {
                    double koef = (double)(i - leftColor.Level) / (double)(rightColor.Level - leftColor.Level);
                    int r = leftColor.Color.R + (int)(koef * (rightColor.Color.R - leftColor.Color.R));
                    int g = leftColor.Color.G + (int)(koef * (rightColor.Color.G - leftColor.Color.G));
                    int b = leftColor.Color.B + (int)(koef * (rightColor.Color.B - leftColor.Color.B));
                    aColor[i] = Color.FromArgb(r, g, b);
                }
                else
                {
                    aColor[i] = leftColor.Color;
                }

            }
            return aColor;
        }

        /// <summary>
        /// Use this to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Gray2Color(this);
        }
    }
}
